/**
 * Copyright 2008 Mathias Doenitz, http://lis.to/
 *
 * This file is part of the lis.to java desktop client. The lis.to java desktop client is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * The lis.to java desktop client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with the lis.to java desktop client.
 * If not, see http://www.gnu.org/licenses/
 */

package listo.utils.logging;

import listo.utils.FileUtils2;
import listo.utils.MiscUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import java.util.Properties;

public class Log4JLog implements Log {

    private static boolean initialized;

    private Logger logger;

    public Log4JLog() {
        if (!initialized) throw new RuntimeException("Not yet initialized. Call Log4JLog.configure first!");
    }

    /**
     * Initializes the log4j logging infrastructure. Uses the given configResource for configuration and
     * and optional log file to override the FileAppender.File.
     *
     * @param configResource the config resource
     * @param customLogFile  if not null, a custom log file. Empty string for disabling file loggin altogether.
     */
    public static void configure(String configResource, String customLogFile) {
        if (initialized) throw new RuntimeException("Already initialized");
        initialized = true;

        if (StringUtils.isNotEmpty(configResource)) {
            try {
                String configText = FileUtils2.readAllTextFromResource(configResource);
                Properties properties = MiscUtils.deserialize(configText);
                if (properties != null) {
                    if (customLogFile != null) {
                        if (customLogFile.length() == 0) {
                            properties.setProperty("log4j.rootLogger",
                                    properties.getProperty("log4j.rootLogger").
                                            replace("FileAppender,", "").
                                            replace("FileAppender", ""));
                        } else {
                            properties.setProperty("log4j.appender.FileAppender.File", customLogFile);
                        }
                    }
                    PropertyConfigurator.configure(properties);
                    return;
                }
            }
            catch (Exception e) {
                if (StringUtils.isNotEmpty(customLogFile)) {
                    FileUtils2.writeAllText("Log configuration error: " + e.getMessage(), customLogFile);
                }
            }
        }

        // no config found, so do a basic configuration
        BasicConfigurator.configure();
    }

    /**
     * Creates a Log4J Logger for the class name of the class performing the log action.
     *
     * @return the logger
     */
    private static Logger getLog4JLogger() {
        //noinspection ThrowableInstanceNeverThrown
        String callingClass = new Throwable().fillInStackTrace().getStackTrace()[2].getClassName();
        return Logger.getLogger(callingClass);
    }

    public void log(Level level, String message, Object... args) {
        switch (level) {
            case TRACE:
                trace(message, args);
                break;
            case DEBUG:
                debug(message, args);
                break;
            case INFO:
                info(message, args);
                break;
            case WARN:
                warn(message, args);
                break;
            case ERROR:
                error(message, args);
                break;
            case FATAL:
                fatal(message, args);
                break;
        }
    }

    public void mark() {
        if (logger == null) logger = getLog4JLogger();
        if (logger.isTraceEnabled()) {
            logger.trace("*** MARK ***");
        }
    }

    public void trace(String message, Object... args) {
        if (logger == null) logger = getLog4JLogger();
        if (logger.isTraceEnabled()) {
            logger.trace(args.length > 0 ? String.format(message, args) : message);
        }
    }

    public void debug(String message, Object... args) {
        if (logger == null) logger = getLog4JLogger();
        if (logger.isDebugEnabled()) {
            logger.debug(args.length > 0 ? String.format(message, args) : message);
        }
    }

    public void info(String message, Object... args) {
        if (logger == null) logger = getLog4JLogger();
        if (logger.isInfoEnabled()) {
            logger.info(args.length > 0 ? String.format(message, args) : message);
        }
    }

    public void warn(String message, Object... args) {
        if (logger == null) logger = getLog4JLogger();
        logger.warn(args.length > 0 ? String.format(message, args) : message);
    }

    public void error(String message, Object... args) {
        if (logger == null) logger = getLog4JLogger();
        logger.error(args.length > 0 ? String.format(message, args) : message);
    }

    public void fatal(String message, Object... args) {
        if (logger == null) logger = getLog4JLogger();
        logger.fatal(args.length > 0 ? String.format(message, args) : message);
    }

    public static Level translate(org.apache.log4j.Level level) {
        switch (level.toInt()) {
            case org.apache.log4j.Level.TRACE_INT:
                return Level.TRACE;
            case org.apache.log4j.Level.DEBUG_INT:
                return Level.DEBUG;
            case org.apache.log4j.Level.INFO_INT:
                return Level.INFO;
            case org.apache.log4j.Level.WARN_INT:
                return Level.WARN;
            case org.apache.log4j.Level.ERROR_INT:
                return Level.ERROR;
            default:
                return Level.FATAL;
        }
    }
}

